package com.practice.xml.xmlpull;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;

import com.practice.xml.Company;
import com.practice.xml.CompanyDefinition;
import com.practice.xml.Employee;
import com.practice.xml.EmployeeDefinition;
import com.practice.xml.XmlProcessor;

public class PullXmlProcessor implements XmlProcessor {

	private XmlPullParserFactory xmlPullParserFactory;

	public PullXmlProcessor() {
		try {
			xmlPullParserFactory = XmlPullParserFactory.newInstance();
		} catch (XmlPullParserException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());
		}
	}

	@Override
	public void createXml(Company company, String filePath) {
		Writer writer = null;
		try {
			writer = new FileWriter(filePath);
			XmlSerializer serializer = xmlPullParserFactory.newSerializer();
			serializer.setOutput(writer);
			createDocument(company, serializer);
			writer.flush();
		} catch (XmlPullParserException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());
		} catch (IOException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());
		} finally {
			if (writer != null) {
				try {
					writer.close();
				} catch (IOException ex) {
					System.out.println(ex.getMessage());
					System.out.println(ex.getStackTrace());
				}
			}
		}
	}

	private void createDocument(Company company, XmlSerializer serializer) throws IOException {
		serializer.startDocument("UTF-8", true);
		
		createCompany(company, serializer);
		
		serializer.endDocument();
	}

	private void createCompany(Company company, XmlSerializer serializer) throws IOException {
		serializer.startTag(null, CompanyDefinition.NAME);
		serializer.attribute(null, CompanyDefinition.ATTRIBUTE_NAME, company.getName());
		
		serializer.startTag(null, CompanyDefinition.ELEMENT_EMPLOYEES);
		createEmployees(company.getEmployees(), serializer);
		serializer.endTag(null, CompanyDefinition.ELEMENT_EMPLOYEES);
		
		serializer.endTag(null, CompanyDefinition.NAME);
	}
	
	private void createEmployees(List<Employee> employees, XmlSerializer serializer) throws IOException {
		for (Employee employee : employees) {
			createEmployee(employee, serializer);
		}
	}

	private void createEmployee(Employee employee, XmlSerializer serializer) throws IOException {
		serializer.startTag(null, EmployeeDefinition.NAME);

		serializer.startTag(null, EmployeeDefinition.ELEMENT_NAME);
		createEmployeeName(employee.getName(), serializer);
		serializer.endTag(null, EmployeeDefinition.ELEMENT_NAME);

		serializer.startTag(null, EmployeeDefinition.ELEMENT_SEX);
		createEmployeeSex(employee.getSex(), serializer);
		serializer.endTag(null, EmployeeDefinition.ELEMENT_SEX);

		serializer.startTag(null, EmployeeDefinition.ELEMENT_AGE);
		createEmployeeAge(employee.getAge(), serializer);
		serializer.endTag(null, EmployeeDefinition.ELEMENT_AGE);

		serializer.endTag(null, EmployeeDefinition.NAME);
	}
	
	private void createEmployeeName(String name, XmlSerializer serializer) throws IOException {
		serializer.text(name);
	}
	
	private void createEmployeeSex(boolean sex, XmlSerializer serializer) throws IOException {
		serializer.text(Boolean.toString(sex));
	}
	
	private void createEmployeeAge(int age, XmlSerializer serializer) throws IOException {
		serializer.text(Integer.toString(age));
	}

	@Override
	public Company parseXml(String filePath) {
		Reader reader = null;
		Company company = null;
		try {
			reader = new FileReader(filePath);
			XmlPullParser parser = xmlPullParserFactory.newPullParser();
			parser.setInput(reader);
			company = parseDocument(parser);
		} catch (XmlPullParserException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());
		} catch (IOException ex) {
			System.out.println(ex.getMessage());
			System.out.println(ex.getStackTrace());
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException ex) {
					System.out.println(ex.getMessage());
					System.out.println(ex.getStackTrace());
				}
			}
		}

		return company;
	}

	private Company parseDocument(XmlPullParser parser) 
			throws IOException, XmlPullParserException {
		Company company = new Company();

		int eventType;
		String nodeName;
		while (true) {
			boolean isParseDone = false;
			eventType = parser.getEventType();
			nodeName = parser.getName();

			switch (eventType) {
			case XmlPullParser.START_DOCUMENT:
				break;
			case XmlPullParser.START_TAG:
				if (CompanyDefinition.NAME.equals(nodeName)) {
					company = parseCompany(parser);
				}
				break;
			case XmlPullParser.END_TAG:
				break;
			case XmlPullParser.END_DOCUMENT:
				isParseDone = true;
			default:
				break;
			}

			if (isParseDone) {
				break;
			} else {
				parser.next();
			}
		}

		return company;
	}
	
	private Company parseCompany(XmlPullParser parser) 
			throws IOException, XmlPullParserException {
		Company company = new Company();
		company.setName(parser.getAttributeValue("", CompanyDefinition.ATTRIBUTE_NAME));
		
		int eventType;
		String nodeName;
		parser.next();
		while (true) {
			boolean isParseDone = false;
			eventType = parser.getEventType();
			nodeName = parser.getName();

			switch (eventType) {
			case XmlPullParser.START_DOCUMENT:
				break;
			case XmlPullParser.START_TAG:
				if (CompanyDefinition.ELEMENT_EMPLOYEES.equals(nodeName)) {
					company.setEmployees(parseEmployees(parser));
				}
				break;
			case XmlPullParser.END_TAG:
				if (CompanyDefinition.NAME.equals(nodeName)) {
					isParseDone = true;
				}
				break;
			default:
				break;
			}

			if (isParseDone) {
				break;
			} else {
				parser.next();
			}
		}

		return company;
	}

	private List<Employee> parseEmployees(XmlPullParser parser) 
			throws IOException, XmlPullParserException {
		List<Employee> employees = new ArrayList<>();

		int eventType;
		String nodeName;
		parser.next();
		while (true) {
			boolean isParseDone = false;
			eventType = parser.getEventType();
			nodeName = parser.getName();
			switch (eventType) {
			case XmlPullParser.START_TAG:
				if (EmployeeDefinition.NAME.equals(nodeName)) {
					employees.add(parseEmployee(parser));
				}
				break;
			case XmlPullParser.END_TAG:
				if (CompanyDefinition.ELEMENT_EMPLOYEES.equals(nodeName)) {
					isParseDone = true;
				}
				break;
			default:
				break;
			}
			
			if (isParseDone) {
				break;
			} else {
				parser.next();
			}
		}

		return employees;
	}
	
	private Employee parseEmployee(XmlPullParser parser) 
			throws IOException, XmlPullParserException {
		Employee employee = new Employee();
		
		int eventType;
		String nodeName;
		parser.next();
		while (true) {
			boolean isParseDone = false;
			eventType = parser.getEventType();
			nodeName = parser.getName();
			switch (eventType) {
			case XmlPullParser.START_TAG:
				if (EmployeeDefinition.ELEMENT_NAME.equals(nodeName)) {
					employee.setName(parseEmployeeName(parser));
				} else if (EmployeeDefinition.ELEMENT_SEX.equals(nodeName)) {
					employee.setSex(parseEmployeeSex(parser));
				} else if (EmployeeDefinition.ELEMENT_AGE.equals(nodeName)) {
					employee.setAge(parseEmployeeAge(parser));
				}
				break;
			case XmlPullParser.END_TAG:
				if (EmployeeDefinition.NAME.equals(nodeName)) {
					isParseDone = true;
				}
				break;
			default:
				break;
			}

			if (isParseDone) {
				break;
			} else {
				parser.next();
			}
		}
		
		return employee;
	}
	
	private String parseEmployeeName(XmlPullParser parser) 
			throws IOException, XmlPullParserException {
		String name = null;

		int eventType;
		String nodeName;
		parser.next();
		while (true) {
			boolean isParseDone = false;
			eventType = parser.getEventType();
			nodeName = parser.getName();
			switch (eventType) {
			case XmlPullParser.TEXT:
				name = parser.getText();
				break;
			case XmlPullParser.END_TAG:
				if (EmployeeDefinition.ELEMENT_NAME.equals(nodeName)) {
					isParseDone = true;
				}

			}
			
			if (isParseDone) {
				break;
			} else {
				parser.next();
			}
		}

		return name;
	}
	
	private boolean parseEmployeeSex(XmlPullParser parser) 
			throws IOException, XmlPullParserException {
		boolean sex = false;

		int eventType;
		String nodeName;
		parser.next();
		while (true) {
			boolean isParseDone = false;
			eventType = parser.getEventType();
			nodeName = parser.getName();
			switch (eventType) {
			case XmlPullParser.TEXT:
				sex = Boolean.parseBoolean(parser.getText());
				break;
			case XmlPullParser.END_TAG:
				if (EmployeeDefinition.ELEMENT_SEX.equals(nodeName)) {
					isParseDone = true;
				}

			}
			
			if (isParseDone) {
				break;
			} else {
				parser.next();
			}
		}

		return sex;
	}
	
	private int parseEmployeeAge(XmlPullParser parser) 
			throws IOException, XmlPullParserException {
		int age = 0;

		int eventType;
		String nodeName;
		parser.next();
		while (true) {
			boolean isParseDone = false;
			eventType = parser.getEventType();
			nodeName = parser.getName();
			switch (eventType) {
			case XmlPullParser.TEXT:
				age = Integer.parseInt(parser.getText());
				break;
			case XmlPullParser.END_TAG:
				if (EmployeeDefinition.ELEMENT_AGE.equals(nodeName)) {
					isParseDone = true;
				}

			}
			
			if (isParseDone) {
				break;
			} else {
				parser.next();
			}
		}

		return age;
	}
}
